home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 18 / develop 18 code / Apple Guide Sample / Source / UAppMo.cp < prev    next >
Encoding:
Text File  |  1994-07-15  |  29.7 KB  |  1,094 lines  |  [TEXT/MPS ]

  1. // Copyright ©1994 Apple Computer, Inc.
  2. // Author: John Powers
  3. // Date:   09-Jun-94
  4.  
  5. // UAppMo.cp
  6. // The derived application class for MoGuide.
  7.  
  8. // Revision history (most recent first):
  9. //
  10. // 09-Jun-94 1.0d2e1 JRP
  11. // Add check for valid art document in TAppMo::ReplyToCoach.
  12. // 17-Mar-94 1.0d1e5 JRP
  13. // First public release (develop issue 18).
  14. //
  15.  
  16. #ifndef __UAPPMO__
  17.     #include "UAppMo.h"
  18. #endif
  19.  
  20. #include <Dialogs.h>
  21.  
  22.         // Segment
  23.  
  24. #pragma segment Main
  25.  
  26. // ------------------------------------------------------------------------
  27. // TAppMo::HandleAECustom
  28. // Handles the custom events.
  29. // The refCon contains our application object.
  30. // Should be in a locked, unpurgeable segment.
  31. //
  32. // Unfortunately, the DTS sample TApplication does not process
  33. // high level events.  We fix that by overriding the EventLoop
  34. // and adding high-level event processing.
  35. //
  36. pascal OSErr
  37. TAppMo::HandleAECustom(AppleEvent& theAppleEvent,
  38.                         AppleEvent& /*theReply*/, long refCon)
  39. {
  40.     OSType        eventId;
  41.     Size        actualSize;
  42.     DescType    returnedType;
  43.     OSErr        err=noErr;
  44.             // Useless without our application object.
  45.     TAppMo* ourApp=(TAppMo*)refCon;
  46.     if(!ourApp)
  47.         return kErrNoAppObject;
  48.             // Get a potential document collaboarator.
  49.     TDocArt* docArt = ourApp->GetDocArt();
  50.             // Get event id.
  51.     err = AEGetAttributePtr(&theAppleEvent, keyEventIDAttr,
  52.                                 typeType, &returnedType,
  53.                                 (Ptr) &eventId, sizeof(eventId),
  54.                                 &actualSize);
  55.     switch (eventId)
  56.     {
  57.         case kAEIdCloseDocArt:
  58.             ourApp->CloseDoc(ourApp->fDocArt);
  59.             break;
  60.         case kAEIdShowDocArt:
  61.             ourApp->ShowArt();
  62.             ourApp->DoAdvance();
  63.             break;
  64.         case kAEIdCloseDocFB:
  65.             ourApp->CloseDoc(ourApp->fDocFB);
  66.             break;
  67.         case kAEIdShowDocFB:
  68.             ourApp->ShowFeedback();
  69.             break;
  70.         case kAEIdReset:
  71.             if(docArt)
  72.                 docArt->SetWantReset();
  73.             break;
  74.         case kAEIdShuffle:
  75.             if(docArt)
  76.                 docArt->SetWantShuffle();
  77.             break;
  78.         case kAEIdQuitApp:
  79.             ourApp->ExitLoop();
  80.             break;
  81.         case kAEIdWaitCollision:
  82.                 // The context check will set the event.
  83.                 // If the event is constant, we could set it
  84.                 // right here and skip the context check.
  85.                 // Otherwise, we need the context check to
  86.                 // send us the event that the author desires.
  87.             if(docArt)
  88.                 docArt->SetWantCollision(true);
  89.             break;
  90.         case kAEIdIgnoreCollision:
  91.                 // Ignore any collision,
  92.                 // turn off flag and clear event.
  93.             if(docArt)
  94.             {
  95.                 docArt->SetWantCollision(false);
  96.                 docArt->SetCollisionEvent(0);
  97.             }
  98.             break;
  99.         case kAEIdAdvance:
  100.                 // Advance on the next open-window event.
  101.             ourApp->SetOkayAdvance(true);
  102.             break;
  103.         default:
  104.             err = errAEEventNotHandled;
  105.             break;
  106.     }
  107.     if(ourApp->fFBEvent)
  108.         ourApp->fFBEvent->SetFlash(eventId);
  109.     return err;
  110.     
  111. }
  112.  
  113. // --------------------------------------------------------------------
  114. // TAppMo::ReplyToCoach
  115. //
  116. // This is a callback invoked by the Coach Apple event handler.
  117. // This member function and the refCon
  118. // was provided by us in the Init call.
  119. //
  120. // Handles replies for Coach queries sent to OurApp.
  121. // Called with the item name and refCon.
  122. // Return the item Rect and a result code.
  123. //
  124. // The name is a zero-terminated string (C-string).
  125. // equalstring requires an A5 world.
  126. //
  127. // Caveats:
  128. //
  129. //        Our app resource map is not available.
  130. //        If you need a resource such as a string,
  131. //        read it in advance and save it in memory.
  132. //
  133. // A static function in main segment.
  134. pascal OSErr
  135. TAppMo::ReplyToCoach(Rect* pItemRect, Ptr pName, long refCon)
  136. {
  137.     OSErr        result=kAGErrItemNotFound;
  138.             // We probably need an A5 world.
  139.     long restoreGlobals = SetA5(*(long*)CurrentA5);
  140.             // Our application object is in the refcon.
  141.     TAppMo* ourApp=(TAppMo*) refCon;
  142.     if(ourApp)
  143.     {
  144.             // Get art document.
  145.         TDocArt* docArt = ourApp->fDocArt;
  146.             // Make sure we have a valid document (may be closed).
  147.         if(docArt)
  148.         {
  149.                 // Ask our document window for the rect of the named item.
  150.             if(docArt->GetLocation(pName, pItemRect))
  151.             {
  152.                 GrafPtr    savePort;
  153.                     // Return the rectangle in global coordinates
  154.                 GetPort(&savePort);
  155.                     // Get the GrafPort.
  156.                 SetPort((GrafPtr) ourApp->fDocArt->GetDocWindow());
  157.                     // Top-left
  158.                 LocalToGlobal((Point*)&(pItemRect->top));
  159.                     // Bottom-right
  160.                 LocalToGlobal((Point*)&(pItemRect->bottom));
  161.                 SetPort(savePort);
  162.                 result = noErr;
  163.             }
  164.         }
  165.             // Feedback - auditory and visual
  166.         if(ourApp->fFBCoach)
  167.             ourApp->fFBCoach->SetFlash(pName);
  168.     } // if(ourApp…
  169.      SetA5(restoreGlobals);
  170.     return(result);
  171. }
  172.  
  173. // ------------------------------------------------------------------------
  174. // TContext::ReplyToContext
  175. // Handles replies for context checks sent to OurApp.
  176. // Called with the data in the context check database object
  177. // and the event you sent in the Init.
  178. // Return the result (true or false).
  179. // A callback.
  180. // A static function in main segment.
  181. pascal OSErr
  182. TContext::ReplyToContext(Ptr pInput, Size inputDataSize,
  183.                             Ptr *ppOutput, Size *pOutputDataSize,
  184.                             AGAppInfoHdl hAppInfo)
  185. {
  186.     OSErr        err=noErr;
  187.     long        inputValue=0;
  188.     short        result=false;
  189.             // We probably need an A5 world.
  190.     long restoreGlobals = SetA5(*(long*)CurrentA5);
  191.             // The input value can be used by either event id.
  192.     if(pInput && inputDataSize>0)
  193.     {
  194.         inputValue = *((long*)pInput);
  195.     }
  196.             // Our application object was passed by us in the refCon.
  197.             // It is returned to us in the refcon of hAppInfo.
  198.     TAppMo* ourApp = (TAppMo*) (**hAppInfo).refCon;
  199.             // Which event id?
  200.     switch ((**hAppInfo).eventId)
  201.     {
  202.                     // kAEIdContextCollision
  203.                     // Set the collision event to the inputValue.
  204.                     // When a collision occurs, the event will
  205.                     // be sent to Apple Guide.  In other words,
  206.                     // Apple Guide is saying "Let me know when
  207.                     // a collision occurs by sending me this event."
  208.                     // A zero input value cancels the event.
  209.                     // Always returns a false result.
  210.         case kAEIdContextCollision:
  211.             if(ourApp->fDocArt)
  212.                 if(ourApp->fDocArt->WantCollision())
  213.                     ourApp->fDocArt->SetCollisionEvent(inputValue);
  214.             result = false;
  215.             break;
  216.                     // kAEIdContextBeep
  217.                     // Return the value (true or false) saved in fBeepReturn.
  218.         case kAEIdContextBeep:
  219.             result = ourApp->fBeepReturn;
  220.             break;
  221.                     // kAEIdContextWindow
  222.                     // Return true if a document window is open and showing.
  223.         case kAEIdContextWindow:
  224.             result = (ourApp->fDocArt)?ourApp->fDocArt->IsWindowVisible():false;
  225.             break;
  226.         default:
  227.                     // An unknown event will be ignored.
  228.             break;
  229.     }
  230.         // Create storage for our result.
  231.     *ppOutput = NewPtr(sizeof(short));
  232.     if(*ppOutput)
  233.     {
  234.             // Set the result and its size.
  235.         **ppOutput = result;
  236.         *pOutputDataSize = sizeof(short);
  237.     }
  238.         // Feedback - auditory and visual
  239.     if(ourApp->fFBContext)
  240.         ourApp->fFBContext->SetFlash((**hAppInfo).eventId);
  241.  
  242.      SetA5(restoreGlobals);
  243.     return err;
  244. }
  245.  
  246.         // Segment
  247.  
  248. #pragma segment MoG1
  249.  
  250. // =========================================================================
  251. // TAppMo
  252. // ------------------------------------------------------------------------
  253. TAppMo::TAppMo()
  254. {
  255. };
  256.  
  257. // ------------------------------------------------------------------------
  258. // TAppMo::AddGuideFiles
  259. // Add the guide files to the mMoGuide menu.
  260. //
  261. // If there are any files, a dashed line is added first,
  262. // followed by the menu name for each file.  We save
  263. // the FSSpec's for easy retrieval when the user chooses
  264. // the menu item.  If any guide files already exist on
  265. // the menu, they are deleted first and a fresh list
  266. // is prepared.
  267. //
  268. void
  269. TAppMo::AddGuideFiles()
  270. {
  271.     short        newFileCount;
  272.     Str63        menuName;
  273.     FSSpec        fileSpec;
  274.     MenuHandle    hmMoGuide = GetMHandle(mMoGuide);
  275.     if(hmMoGuide)
  276.     {
  277.             // If we already have a file list, delete it.
  278.         if(this->fhFileList)
  279.         {
  280.                 // We do, dispose of it and its menu items.
  281.             DisposeHandle(this->fhFileList);
  282.             while(this->fFileCount--)
  283.             {
  284.                     // Delete the item after the dash line.
  285.                 DelMenuItem(hmMoGuide, iLastMenuItem+2);
  286.             }
  287.                 // Delete dashed line.
  288.             DelMenuItem(hmMoGuide, iLastMenuItem+1);
  289.         }
  290.             // Set the search directory for guide files.
  291.         short    vRefNum = (-*(short*)SFSaveDisk);
  292.         long    dirID = (*(long*)CurDirStore);
  293.         Boolean    wantMixin = false;
  294.         newFileCount = AGFileGetDBCount(vRefNum, dirID,
  295.                                         kAGFileDBTypeAny, wantMixin);
  296.                                     
  297.         if(newFileCount>0)
  298.         {
  299.                 // Create a new list of file FSSpecs.
  300.             this->fhFileList = NewHandle(newFileCount*sizeof(FSSpec));
  301.             if(this->fhFileList)
  302.             {
  303.                     // Add dashed line.
  304.                 AppendMenu(hmMoGuide, "\p-");
  305.                     // Get each file, add to menu and to list.
  306.                 this->fFileCount = newFileCount;
  307.                 for(short i=1; i<=newFileCount; i++)
  308.                 {
  309.                         // Use same vRefNum, dirID, and wantMixin
  310.                         // that was set for the count.
  311.                     if(AGFileGetIndDB(vRefNum, dirID,
  312.                                     kAGFileDBTypeAny, wantMixin,
  313.                                     i, &fileSpec)==noErr)
  314.                     {
  315.                         if(AGFileGetDBMenuName(&fileSpec, menuName)==noErr)
  316.                         {
  317.                                 // We have a guide file,
  318.                                 // get it's menu name and add to menu.
  319.                             AppendMenu(hmMoGuide, menuName);
  320.                             ((FSSpec*)(*this->fhFileList))[i-1] = fileSpec;
  321.                         }
  322.                     }
  323.                 } // for(short…
  324.             } // if(hFileMenu…
  325.         } // if(newFileCount…
  326.     } // if(hmMoGuide…
  327. }
  328.  
  329. // --------------------------------------------------------------------------
  330. // TAppMo::AdjustMenus
  331. //
  332. void
  333. TAppMo::AdjustMenus()            // override
  334. {
  335.             // Inherit first.
  336.     TApp::AdjustMenus();
  337.             // Our menus.
  338.     MenuHandle    hmMoGuide=GetMHandle(mMoGuide);
  339.     MenuHandle    hmWindow=GetMHandle(mWindow);
  340.             // Always enabled
  341.     EnableItem(hmMoGuide, iFiles);
  342.             // Set default case.
  343.     DisableItem(hmMoGuide, iStartGuide);
  344.     DisableItem(hmMoGuide, iQuitGuide);
  345.     DisableItem(hmMoGuide, iOpenDefault);
  346.     DisableItem(hmMoGuide, iClose);
  347.             // If Apple Guide is installed, then we can do lots of things.
  348.     if(gAGuideAvailable)
  349.     {
  350.         AGStatus status = AGGetStatus();
  351.                 // Permit starting it if it's not already started.
  352.         if(status==kAGIsNotRunning)
  353.             EnableItem(hmMoGuide, iStartGuide);
  354.                 // If Apple Guide is sleeping, then we can make it quit.
  355.         else if(status==kAGIsSleeping)
  356.             EnableItem(hmMoGuide, iQuitGuide);
  357.                 // If we have default help available, then we can open it.
  358.         if(AGGetAvailableDBTypes() & kAGDBTypeBitHelp)
  359.             EnableItem(hmMoGuide, iOpenDefault);
  360.                 // If we have a non-nil this->fGuideRefNum,
  361.                 // then a database is open. Permit closing.
  362.                 // this->fGuideRefNum is tracked in TApp::AdjustMenus.
  363.         if(this->fGuideRefNum)
  364.             EnableItem(hmMoGuide, iClose);
  365.                 // Enable any databases that we may have added.
  366.         for(short i=0; i<this->fFileCount; i++)
  367.             EnableItem(hmMoGuide, iLastMenuItem+i+2);
  368.     }
  369.             // Always available.
  370.             // Art-related items.
  371.     EnableItem(hmWindow, iWinArt);
  372.         // Can reset objects if a window is visible.
  373.     if(this->fDocArt)
  374.     {
  375.         EnableItem(hmWindow, iResetArt);
  376.         EnableItem(hmWindow, iShuffleArt);
  377.     }
  378.     else
  379.     {
  380.         DisableItem(hmWindow, iResetArt);
  381.         DisableItem(hmWindow, iShuffleArt);
  382.     }
  383.             // Always available.
  384.     EnableItem(hmWindow, iFeedback);
  385.     EnableItem(hmWindow, iContextTrue);
  386.     CheckItem(hmWindow, iContextTrue, this->fBeepReturn);
  387.     EnableItem(hmWindow, iContextBeep);
  388.     if(this->fFBContext)
  389.         CheckItem(hmWindow, iContextBeep, this->fFBContext->GetBeep());
  390.  
  391. }
  392.  
  393. // ------------------------------------------------------------------------
  394. // TAppMo::CheckFrontWindow
  395. // Check to see if Apple Guide has a window showing.
  396. // Update the miscellaneous feedback with the result.
  397. // We look for the front window of the current database
  398. // which is not necessarily our own. If we wanted just
  399. // our own database, we would pass this->fGuideRefNum
  400. // instead of kAGFrontDatabase.
  401. //
  402. void
  403. TAppMo::CheckFrontWindow()
  404. {
  405.     Str255 string;
  406.     short index=kStrNone;
  407.     if(gAGuideAvailable)
  408.     {
  409.             // If Apple Guide is available, then the choices
  410.             // are nothing (Apple Guide is not running),
  411.             // sleeping, access window, or presentation window.
  412.         if(AGGetStatus()!=kAGIsNotRunning)
  413.         {
  414.             AGWindowKind kind = AGGetFrontWindowKind(kAGFrontDatabase);
  415.                 // Clarity, not conciseness.
  416.             if(kind==kAGNoWindow)
  417.                 index = kStrNoneSleeping;
  418.             else if(kind==kAGAccessWindow)
  419.                 index = kStrAccess;
  420.             else if(kind==kAGPresentationWindow)
  421.                 index = kStrPresentation;
  422.         }
  423.     }
  424.         // Update FBMisc if there is a change.
  425.         // It flashes every time we update it.
  426.         // We force an update every kMaxStatusUpdateCnt cycles.
  427.     this->fLastStatusCnt--;
  428.     if(this->fLastStatusCnt==0 || index!=this->fLastMiscIndex)
  429.     {
  430.         GetIndString(string, kFrontWindowStrId, index);
  431.             // If we are in the idle loop, we must check this->fFBMisc
  432.             // validity in case it hasn't been created yet.
  433.         if(this->fFBMisc)
  434.         {
  435.             this->fFBMisc->SetFlash(string);
  436.             this->fLastMiscIndex = index;
  437.         }
  438.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  439.     }
  440. }
  441.  
  442. // ------------------------------------------------------------------------
  443. // TAppMo::DoAdvance
  444. // If an advance is wanted, do it.
  445. // This is a one-shot event.
  446. // We send a go-back event because we assume that Apple Guide
  447. // is showing an oops panel.
  448. void
  449. TAppMo::DoAdvance()
  450. {
  451.     if(gAGuideAvailable && this->fOkayAdvance)
  452.     {
  453.         AlertIfError(AGGeneral(this->fGuideRefNum, kAGEventReturnBack));
  454.     }
  455.     this->fOkayAdvance = false;
  456. }
  457.  
  458. // ------------------------------------------------------------------------
  459. // TAppMo::DoGoAway
  460. //
  461. // This is the close side of TAppMo::ShowArt, ShowClipboard, ShowFeedback.
  462. //
  463. void
  464. TAppMo::DoGoAway()
  465. {
  466.             // Clear our local document record if
  467.             // it's object is going away.
  468.     if(this->fCurDoc==this->fDocFB)
  469.     {
  470.         this->fDocFB = nil;
  471.         this->fFBContext->SetDoc(nil);
  472.         this->fFBCoach->SetDoc(nil);
  473.         this->fFBEvent->SetDoc(nil);
  474.         this->fFBMisc->SetDoc(nil);
  475.     }
  476.     else if(this->fCurDoc==this->fDocArt)
  477.         this->fDocArt = nil;
  478.             // Inherit go-away action from TApp and TApplication
  479.     TApp::DoGoAway();
  480. }
  481.  
  482. // ------------------------------------------------------------------------
  483. // TAppMo::DoIdle
  484. // Do our idle and deferred events.
  485. //
  486. void
  487. TAppMo::DoIdle()        // override
  488. {
  489.         // Inherit to do TApp Idle.
  490.     TApp::DoIdle();
  491.         // Document actions.
  492.     if(this->fDocArt)
  493.         this->fDocArt->DoIdle();
  494.         // Feedback about context-checking.
  495.     if(this->fFBContext)
  496.         this->fFBContext->DoIdle();
  497.         // Feedback about Coach objects.
  498.     if(this->fFBCoach)
  499.         this->fFBCoach->DoIdle();
  500.         // Feedback about Apple events.
  501.     if(this->fFBEvent)
  502.         this->fFBEvent->DoIdle();
  503.         // Check for Apple Guide front window.
  504.     this->CheckFrontWindow();
  505.         // Miscellaneous feedback.
  506.     if(this->fFBMisc)
  507.         this->fFBMisc->DoIdle();
  508. }
  509.  
  510. // ------------------------------------------------------------------------
  511. // TAppMo::DoMenuCommand
  512. // This is called when an item is chosen from the menu bar (after calling
  513. // MenuSelect or MenuKey). It does the right thing for each command.
  514. //
  515. // TAppMo::AdjustMenus enables the menu items
  516. // depending on whether or not guide is available.
  517. // Consequently, we don't have to check here to see
  518. // if guide is available because, if it isn't, the item
  519. // won't be choosen.
  520. //
  521. void
  522. TAppMo::DoMenuCommand(short menuID, short menuItem)        //  override
  523. {
  524.     FSSpec    fileSpec;
  525.     OSErr    err=noErr;
  526.     switch (menuID)
  527.     {
  528.         case mMoGuide:
  529.             switch(menuItem)
  530.             {
  531.                 case iStartGuide:
  532.                     AlertIfError(AGStart());
  533.                     break;
  534.                 case iQuitGuide:
  535.                     AlertIfError(AGQuit());
  536.                     break;
  537.                 case iOpenDefault:
  538.                     AlertIfError(this->OpenGuideDatabase(kAGDefault));
  539.                     break;
  540.                 case iClose:
  541.                     err = AGClose(&this->fGuideRefNum);
  542.                     if(err==kAGErrDatabaseNotOpen)
  543.                         this->fGuideRefNum = nil;
  544.                     break;
  545.                 case iFiles:
  546.                     this->AddGuideFiles();
  547.                     break;
  548.                 default:
  549.                         // Must be one of the guide file items that we added.
  550.                         // Get its FSSpec from the list and open it.
  551.                     if(this->fhFileList)
  552.                     {
  553.                         fileSpec = ((FSSpec*)(*this->fhFileList))[menuItem-iLastMenuItem-2];
  554.                         AlertIfError(this->OpenGuideDatabase(&fileSpec));
  555.                     }
  556.                     break;
  557.             } // switch
  558.             break;
  559.         case mWindow:
  560.             switch(menuItem)
  561.             {
  562.                 case iWinArt:
  563.                     err = this->SendEventToSelf(kAEIdShowDocArt);
  564.                     break;
  565.                 case iFeedback:
  566.                     err = this->SendEventToSelf(kAEIdShowDocFB);
  567.                     break;
  568.                 case iContextTrue:
  569.                     this->fBeepReturn = !this->fBeepReturn;
  570.                     break;
  571.                 case iContextBeep:
  572.                     if(this->fFBContext)
  573.                         this->fFBContext->SetBeep(!this->fFBContext->GetBeep());
  574.                     break;
  575.                 case iResetArt:
  576.                     err = this->SendEventToSelf(kAEIdReset);
  577.                     break;
  578.                 case iShuffleArt:
  579.                     err = this->SendEventToSelf(kAEIdShuffle);
  580.                     break;
  581.                 default:
  582.                     break;
  583.             } // switch
  584.             break;
  585.         default:
  586.             break;
  587.     } // switch
  588.             // Inherit for the Apple, File, and Edit menus.
  589.     TApp::DoMenuCommand(menuID, menuItem);
  590.     AlertIfError(err);
  591. } // DoMenuCommand
  592.  
  593. // ---------------------------------------------------------------------
  594. // TAppMo::Init
  595. // Do the things that our derived application class requires.
  596. // Basic initialization, before we start.
  597. // We store our application object in the core event handler
  598. // refCon so that the handler can use it.  Avoids making it a global.
  599. // Return noErr if successful.
  600. OSErr
  601. TAppMo::Init()
  602. {
  603.             // Inherit
  604.     OSErr err = TApp::Init();
  605.     if(err==noErr)
  606.     {
  607.             // Override the menubar ID.
  608.         this->fMenuBarID = rMenuBarMo;
  609.             // Clear our collaborators because we'll not want to use
  610.             // them until after we get the core startup event.
  611.                 // Clear handler refNums.
  612.         this->fCoachRefNum = nil;
  613.         this->fContext = nil;
  614.         this->fFBContext = nil;
  615.         this->fFBCoach = nil;
  616.         this->fFBEvent = nil;
  617.         this->fFBMisc = nil;
  618.         this->fDocArt = nil;
  619.         this->fDocFB = nil;
  620.             // Also clear our variables or we may get a bus error
  621.             // at the first DoIdle (before we get the start event.)
  622.         this->fhFileList = nil;
  623.         this->fFileCount = 0;
  624.         this->fBeepReturn = false;
  625.         this->fOkayAdvance = false;
  626.         this->fLastMiscIndex = 0;
  627.             // Remove the core event handler installed in TApp.
  628.         (void) AERemoveEventHandler(kCoreEventClass,
  629.                                 typeWildCard,
  630.                                 kHandlerNotRequired,
  631.                                 kIsNotSysHandler);
  632.             // Replace with a core event handler which
  633.             // uses the same handler, but passes TAppMo
  634.             // in the refCon instead of TApp.  This
  635.             // insures that TAppMo::Start is invoked.
  636.         err = AEInstallEventHandler(kCoreEventClass,
  637.                                 typeWildCard,
  638.                                 (EventHandlerProcPtr) TApp::HandleAECore,
  639.                                 (long)this,
  640.                                 kIsNotSysHandler);
  641.             // Install our custom event handler.
  642.             // We put the TAppMo object in the refCon.
  643.         err = AEInstallEventHandler(kAEClassCustom,
  644.                                 typeWildCard,
  645.                                 (EventHandlerProcPtr) TAppMo::HandleAECustom,
  646.                                 (long)this,
  647.                                 kIsNotSysHandler);
  648.             // Initialize our status update cycle counter.
  649.         this->fLastStatusCnt = kMaxStatusUpdateCnt;
  650.             // Starting is done in TApp::HandleAECore.
  651.             // We wait until that happens before proceeding.
  652.     }
  653.     return err;
  654. }
  655.  
  656. // ------------------------------------------------------------------------
  657. // TAppMo::Quit
  658. // We're quitting, so delete everything.
  659. // All of this probably goes away in the application heap anyway,
  660. // we're just being compulsively tidy.
  661. //
  662. void
  663. TAppMo::Quit()
  664. {
  665.     if(gAGuideAvailable)
  666.     {
  667.                 // Remove Coach and context handlers.
  668.         AlertIfError(AGRemoveCoachHandler(&this->fCoachRefNum));
  669.     }
  670.             // Delete our context object.
  671.     if(this->fContext)
  672.     {
  673.         AlertIfError(this->fContext->Remove());
  674.         delete this->fContext;
  675.     }
  676.             // Delete our flasher objects.
  677.     if(this->fFBContext)
  678.         delete this->fFBContext;
  679.     if(this->fFBCoach)
  680.         delete this->fFBCoach;
  681.     if(this->fFBEvent)
  682.         delete this->fFBEvent;
  683.     if(this->fFBMisc)
  684.         delete this->fFBMisc;
  685.             // Remove list of guide files.
  686.     if(this->fhFileList)
  687.         DisposeHandle(this->fhFileList);
  688.             // Remove custom event handler.
  689.     (void) AERemoveEventHandler(kAEClassCustom,
  690.                                 typeWildCard,
  691.                                 kHandlerNotRequired,
  692.                                 kIsNotSysHandler);
  693.             // Inherit.
  694.     TApp::Quit();
  695. }
  696.  
  697. // ------------------------------------------------------------------------
  698. // TAppMo::ShowArt
  699. // Opens the art window.
  700. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  701. //
  702. // The art is specified in a resource.
  703. // The art resource id matches the id of its window.
  704. // To add another window, specify a new resource id,
  705. // define the new art resource, and instantiate a new TDocArt.
  706. //
  707. void
  708. TAppMo::ShowArt()
  709. {
  710.     if(this->fDocArt==nil)
  711.     {
  712.             // Art window is not present.
  713.             // Create a document and window for the art.
  714.         this->fDocArt = new TDocArt(kArt1WindResID);
  715.         if(this->fDocArt)
  716.         {
  717.                 // Add new document to document list.
  718.             this->fDocList->AddDoc((TDocument*) this->fDocArt);
  719.                 // Update current document and window pointer.
  720.             this->fCurDoc = this->fDocArt;
  721.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  722.             SetPort(this->fWhichWindow);
  723.                 // Initialize art document window.
  724.             AlertIfError(this->fDocArt->Init(kArt1WindResID));
  725.                 // Set the application object as a collaborator.
  726.             this->fDocArt->SetApp(this);
  727.         }
  728.     }
  729.         // Better show it, or at least bring it to the front.
  730.     this->fDocArt->Show();
  731.             // Invalidate window so that it will be updated and drawn.
  732.     this->fDocArt->Invalidate();
  733. }
  734.  
  735. // ------------------------------------------------------------------------
  736. // TAppMo::ShowFeedback
  737. // Opens the feedback window.
  738. // The document window is closed by the go-away box (TAppMo::DoGoAway).
  739. //
  740. void
  741. TAppMo::ShowFeedback()
  742. {
  743.     if(this->fDocFB==nil)
  744.     {
  745.             // Feedback window is not present.
  746.             // Create a document and window for the feedback.
  747.         this->fDocFB = new TDocFB(kFeedbackWinResID);
  748.         if(this->fDocFB)
  749.         {
  750.                 // Add new document to document list.
  751.             this->fDocList->AddDoc((TDocument*) this->fDocFB);
  752.                 // Update current document and window pointer.
  753.             this->fCurDoc = this->fDocFB;
  754.             this->fWhichWindow = this->fCurDoc->GetDocWindow();
  755.             SetPort(this->fWhichWindow);
  756.                     // Initialize feedback document window.
  757.             AlertIfError(this->fDocFB->Init());
  758.                 // Set the application object as a collaborator.
  759.             this->fDocFB->SetApp(this);
  760.                 // Set document window collaborator for feedback.
  761.             this->fFBContext->SetDoc(this->fDocFB);
  762.             this->fFBCoach->SetDoc(this->fDocFB);
  763.             this->fFBEvent->SetDoc(this->fDocFB);
  764.             this->fFBMisc->SetDoc(this->fDocFB);
  765.         }
  766.     }
  767.         // Better show it, or at least bring it to the front.
  768.     this->fDocFB->Show();
  769.             // Invalidate window so that it will be updated and drawn.
  770.     this->fDocFB->Invalidate();
  771. }
  772.  
  773. // ---------------------------------------------------------------------
  774. // TAppMo::Start
  775. // Startup the application.
  776. // The Init is done in main.  The Start comes with the oapp or odoc event.
  777. // Return noErr if successful.
  778. OSErr
  779. TAppMo::Start()
  780. {
  781.             // Inherit.
  782.     OSErr err = TApp::Start();
  783.     {
  784.                 // Reply to Coach mark queries.
  785.                 // Pass along the TApp object.
  786.                 // Setup our callbacks for context-check queries.
  787.                 // Pass along the TApp object in the refCon.
  788.         if(gAGuideAvailable)
  789.         {
  790.             err = AGInstallCoachHandler((CoachReplyProcPtr)TAppMo::ReplyToCoach,
  791.                                         (long)this, &this->fCoachRefNum);
  792.             if(err!=noErr)
  793.                 return err;
  794.             this->fContext = new TContext;
  795.             if(this->fContext)
  796.                 err = this->fContext->Install((long)this);
  797.             else
  798.                 err = kErrNoContextObject;
  799.             if(err!=noErr)
  800.                 return err;
  801.         }
  802.                 // List of guide files.
  803.         this->fhFileList = nil;
  804.         this->fFileCount = 0;
  805.                 // Don't want a panel advance yet.
  806.         this->fOkayAdvance = false;
  807.                 // Return a true or false to the beep context check query?
  808.         this->fBeepReturn = false;
  809.                 // Feedback (the Flasher).
  810.         this->fFBContext = new TFeedback;
  811.         if(this->fFBContext)
  812.             this->fFBContext->Init(this, dataCC);
  813.         this->fFBCoach = new TFeedback;
  814.         if(this->fFBCoach)
  815.             this->fFBCoach->Init(this, dataCH);
  816.         this->fFBEvent = new TFeedback;
  817.         if(this->fFBEvent)
  818.             this->fFBEvent->Init(this, dataEV);
  819.         this->fFBMisc = new TFeedback;
  820.         if(this->fFBMisc)
  821.         {
  822.             this->fFBMisc->Init(this, dataTP);
  823.                 // Set an infinite interval for FBMisc.
  824.             this->fFBMisc->SetInterval(0);
  825.         }
  826.                 // Show the feedback window/document.
  827.         err = this->SendEventToSelf(kAEIdShowDocFB);
  828.     }
  829.     return err;
  830. }
  831.  
  832. // =========================================================================
  833. // TContext
  834. // ------------------------------------------------------------------------
  835. TContext::TContext()
  836. {
  837.     this->fHandlerCnt = 0;
  838. }
  839.  
  840. // ------------------------------------------------------------------------
  841. TContext::~TContext()
  842. {
  843.         // If we still have handlers installed, remove them.
  844.     if(this->fHandlerCnt>0)
  845.         (void) this->Remove();
  846. }
  847.  
  848. // ------------------------------------------------------------------------
  849. // TContext::Install
  850. // Install context-check handlers.
  851. // The appObj is the application object
  852. // used in the reply to access the application.
  853. // If any error occurs, return it.
  854. //
  855. OSErr
  856. TContext::Install(long appObj)
  857. {
  858.     OSErr result=noErr;
  859.     OSErr eachResult;
  860.         // Define and install context-check handlers.
  861.         // If we still have handlers installed, remove them.
  862.     if(this->fHandlerCnt>0)
  863.         result = this->Remove();
  864.     this->fHandlerCnt = kContextHandlerCnt;
  865.     this->fContext[0].eventID = kAEIdContextCollision;
  866.     this->fContext[1].eventID = kAEIdContextBeep;
  867.     this->fContext[2].eventID = kAEIdContextWindow;
  868.     for(short i=0; i<this->fHandlerCnt; i++)
  869.     {
  870.             // Install handler.
  871.             // If an error, the refNum will be set to nil by Apple Guide.
  872.         eachResult = AGInstallContextHandler(
  873.                                         (ContextReplyProcPtr)TContext::ReplyToContext,
  874.                                         this->fContext[i].eventID,
  875.                                         appObj,
  876.                                         &this->fContext[i].refNum);
  877.             // Track only errors in result.
  878.         result = (eachResult!=noErr)?eachResult:result;
  879.     }
  880.     return result;
  881. }
  882.  
  883. // ------------------------------------------------------------------------
  884. // TContext::Remove
  885. // Remove context-check handlers.
  886. // If any error occurs, return it.
  887. //
  888. OSErr
  889. TContext::Remove()
  890. {
  891.     OSErr result=noErr;
  892.     OSErr eachResult;
  893.     for(short i=0; i<this->fHandlerCnt; i++)
  894.     {
  895.         eachResult = AGRemoveContextHandler(&this->fContext[i].refNum);
  896.             // Track only errors in result.
  897.         result = (eachResult!=noErr)?eachResult:result;
  898.     }
  899.     this->fHandlerCnt = 0;
  900.     return result;
  901. }
  902.  
  903. // =========================================================================
  904. // TTypeStr
  905. // ------------------------------------------------------------------------
  906. TTypeStr::TTypeStr(OSType theType)
  907. {
  908.     TypeToChar toConvert;
  909.     toConvert.type = theType;
  910.             // Convert stored OSType to pstring.
  911.             // Replace zeros with spaces.
  912.     this->fString[0] = 4;
  913.     for(short i=0; i<4; i++)
  914.         this->fString[i+1] = (toConvert.ch[i])?toConvert.ch[i]:' ';
  915. }
  916.  
  917. // ------------------------------------------------------------------------
  918. // TTypeStr::String
  919. StringPtr
  920. TTypeStr::String()
  921. {
  922.     return &this->fString[0];
  923. }
  924.  
  925. // =========================================================================
  926. // TFeedback
  927. // ------------------------------------------------------------------------
  928. TFeedback::TFeedback()
  929. {
  930.     this->fShowTime = 0;
  931.     this->fInterval = kIntervalShow;
  932.     this->fCurrent = 0;
  933.     this->fPrevious = 0;
  934.     this->fShow = false;
  935.     this->fBeep = false;
  936.     this->fOurApp = nil;
  937.     this->fDocFB = nil;
  938. }
  939.  
  940. // ------------------------------------------------------------------------
  941. TFeedback::~TFeedback()
  942. {
  943. }
  944.  
  945. // ------------------------------------------------------------------------
  946. // TFeedback::DoIdle
  947. // Do any action required during the idle processing.
  948. //
  949. void
  950. TFeedback::DoIdle()
  951. {
  952.     this->Show();
  953. }
  954.  
  955. // ------------------------------------------------------------------------
  956. // TFeedback::Draw
  957. void
  958. TFeedback::Draw(OSType toDraw, short mode)
  959. {
  960.     TTypeStr flashChar = toDraw;
  961.             // Draw it in the feedback window (if present).
  962.     if(this->fDocFB)
  963.         this->fDocFB->DrawData(flashChar.String(), mode, this->fWhichData);
  964. }
  965.  
  966. // ------------------------------------------------------------------------
  967. // TFeedback::GetBeep
  968. // Return the want-beep flag.
  969. Boolean
  970. TFeedback::GetBeep()
  971. {
  972.     return this->fBeep;
  973. }
  974.  
  975. // ------------------------------------------------------------------------
  976. // TFeedback::Init
  977. // Set the application collaborator (and any others you might want to add.)
  978. void
  979. TFeedback::Init(TAppMo* theApp, short whichData)
  980. {
  981.         // The collaborator
  982.     this->fOurApp = theApp;
  983.         // The data
  984.     this->fWhichData = whichData;
  985. }
  986.  
  987. // ------------------------------------------------------------------------
  988. // TFeedback::SetBeep
  989. // Set the want-beep flag.
  990. void
  991. TFeedback::SetBeep(Boolean wantBeep)
  992. {
  993.     this->fBeep = wantBeep;
  994. }
  995.  
  996. // ------------------------------------------------------------------------
  997. // TFeedback::SetFlash
  998. void
  999. TFeedback::SetFlash(OSType theFlash)
  1000. {
  1001.     this->fCurrent = theFlash;
  1002.     this->fShow = true;
  1003. }
  1004.  
  1005. // ------------------------------------------------------------------------
  1006. // TFeedback::SetFlash
  1007. // Accept a char ptr, convert its first four characters to an OSType.
  1008. void
  1009. TFeedback::SetFlash(Ptr theFlash)
  1010. {
  1011.     CharToType    flashChar;
  1012.     flashChar.type = '    ';
  1013.     for(short i=0; i<4; i++)
  1014.         if(theFlash[i]!=0)
  1015.             flashChar.ch[i] = theFlash[i];
  1016.     this->fCurrent = flashChar.type;
  1017.     this->fShow = true;
  1018. }
  1019.  
  1020. // ------------------------------------------------------------------------
  1021. // TFeedback::SetFlash
  1022. // Accept a pascal string, convert its first four characters to an OSType.
  1023. void
  1024. TFeedback::SetFlash(Str255 theFlash)
  1025. {
  1026.     short len = (theFlash[0]>4)?4:theFlash[0];
  1027.     CharToType    flashChar;
  1028.     flashChar.type = '    ';
  1029.     for(short i=0; i<len; i++)
  1030.         flashChar.ch[i] = theFlash[i+1];
  1031.     this->fCurrent = flashChar.type;
  1032.     this->fShow = true;
  1033. }
  1034.  
  1035. // ------------------------------------------------------------------------
  1036. // TFeedback::Show
  1037. // Show anything new.
  1038. // When interval is up, erase.
  1039. //
  1040. void
  1041. TFeedback::Show()
  1042. {
  1043.     short drawThisCycle=true;
  1044.     if(this->fShow)
  1045.     {
  1046.         if(this->fPrevious)
  1047.         {
  1048.                 // Something is up there, erase it.
  1049.             this->Draw(this->fPrevious, kErase);
  1050.                 // Is this a repeat of the same item?
  1051.             if(this->fPrevious==this->fCurrent)
  1052.             {
  1053.                     // Same item, don't draw this cycle.
  1054.                     // Delay one cycle to make it "flash".
  1055.                     // Unless the sysbeep is on, that will
  1056.                     // give enough delay to make it flash.
  1057.                 drawThisCycle = this->fBeep;
  1058.             }
  1059.                 // Clear our previous item, it was erased.
  1060.             this->fPrevious = 0;
  1061.         }
  1062.             // Auditory feedback and delay between erase and draw.
  1063.         if(this->fBeep)
  1064.         {
  1065.             SysBeep(1);
  1066.         }
  1067.             // Show.
  1068.         if(drawThisCycle)
  1069.         {
  1070.             this->fShow = false;
  1071.             this->fShowTime = TickCount();
  1072.             this->Draw(this->fCurrent, kPaint);
  1073.             this->fPrevious = this->fCurrent;
  1074.         }
  1075.     }
  1076.     else
  1077.     {
  1078.             // Nothing new to show.
  1079.             // Only erase previous if we have a positive interval.
  1080.         if(this->fPrevious && this->fInterval)
  1081.         {
  1082.             if(TickCount()>this->fShowTime+this->fInterval)
  1083.             {
  1084.                 // Counter timed out and no new stuff.
  1085.                 // Erase old stuff.
  1086.                     // Something is up there, erase it.
  1087.                 this->Draw(this->fPrevious, kErase);
  1088.                 this->fPrevious = 0;
  1089.             }
  1090.         }
  1091.     }
  1092. }
  1093.     
  1094.